#include "stm32f4xx_hal.h"
#include "bsp_usart_fifo.h"
#include "usart.h"

#if USART1_FIFO_EN == 1
	UART_T g_tUart1;
	uint8_t g_TxBuf1[USART1_TX_BUF_SIZE];				// 发送缓冲区
	uint8_t g_RxBuf1[USART1_RX_BUF_SIZE];				// 接收缓冲区
#endif

#if USART2_FIFO_EN == 1
	UART_T g_tUart2;
	uint8_t g_TxBuf2[USART2_TX_BUF_SIZE];				// 发送缓冲区
	uint8_t g_RxBuf2[USART2_RX_BUF_SIZE];				// 接收缓冲区	
#endif

#if USART3_FIFO_EN == 1
	UART_T g_tUart3;
#endif

static void InitHardUsart(void);
static void UsartVarInit(void);

void bsp_InitUsart(void)
{
	// 初始化串口变量，要放在串口硬件初始化之前
	UsartVarInit();
	// 硬件初始化
	InitHardUsart();
	// 485使能引脚
	RS485_InitTXE();
}

/* 初始化串口硬件参数 */
static void InitHardUsart(void)
{
#if USART1_FIFO_EN == 1
	MX_USART1_UART_Init();
#endif

#if USART2_FIFO_EN == 1
	MX_USART2_UART_Init();
#endif
}

/* 485发送使能引脚配置 */
void RS485_InitTXE(void)
{
	GPIO_InitTypeDef GPIO_InitStruct = {0};

	RS485_RCC_TXEN_CLK_ENABLE();

	GPIO_InitStruct.Pin = RS485_TXEN_Pin;
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
	GPIO_InitStruct.Pull = GPIO_NOPULL;
	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
	HAL_GPIO_Init(RS485_TXEN_GPIO_Port, &GPIO_InitStruct);
}

/* 发送前将RS485设为发送状态 */
void RS485_SendBefore(void)
{
	RS485_TX_EN();
}

/* 发送接收后将RS485置为默认接收状态 */
void RS485_SendOver(void)
{
	RS485_RX_EN();
}

/* RS485发送一串数据 */
void RS485_SendBuf(uint8_t * _ucBuf, uint16_t _usLen)
{
	comSendBuf(COM2, _ucBuf, _usLen);
}

/* RS485接收新的数据 */
void RS485_ReceiveNew(uint8_t _byte)
{
	MODS_ReceiveNew(_byte);
}

/* 初始化串口的相关变量 */
static void UsartVarInit(void)
{
#if USART1_FIFO_EN == 1
	g_tUart1.uartHandle = &huart1;
	g_tUart1.pTxBuf = g_TxBuf1;
	g_tUart1.pRxBuf = g_RxBuf1;
	g_tUart1.usTxBufSize = USART1_TX_BUF_SIZE;
	g_tUart1.usRxBufSize = USART1_RX_BUF_SIZE;
	g_tUart1.usTxWrite = 0;		//	缓冲区指针初始化为0
	g_tUart1.usTxRead = 0;
	g_tUart1.usTxCount = 0;
	g_tUart1.usRxWrite = 0;
	g_tUart1.usRxRead = 0;
	g_tUart1.usRxCount = 0;
	g_tUart1.SendBefore = 0;
	g_tUart1.SendOver = 0;
	g_tUart1.ReveiveNew = 0;
#endif

#if USART2_FIFO_EN == 1
	g_tUart2.uartHandle = &huart2;
	g_tUart2.pTxBuf = g_TxBuf2;
	g_tUart2.pRxBuf = g_RxBuf2;
	g_tUart2.usTxBufSize = USART2_TX_BUF_SIZE;
	g_tUart2.usRxBufSize = USART2_RX_BUF_SIZE;
	g_tUart2.usTxWrite = 0;		//	缓冲区指针初始化为0
	g_tUart2.usTxRead = 0;
	g_tUart2.usTxCount = 0;
	g_tUart2.usRxWrite = 0;
	g_tUart2.usRxRead = 0;
	g_tUart2.usRxCount = 0;
	
	g_tUart2.SendBefore = RS485_SendBefore;
	g_tUart2.SendOver = RS485_SendOver;
	g_tUart2.ReveiveNew = RS485_ReceiveNew;	
#endif
}

/**
 * @brief		枚举COM口转换为对应的串口指针
 * @param		_ucPort: COM口
 * @retval	串口指针
 * @note
 */
UART_T* ComToUart(COM_PORT_E _ucPort)
{
	if(_ucPort == COM1)
	{
		#if USART1_FIFO_EN == 1
			return &g_tUart1;
		#else
			return 0;
		#endif
	}
	else if(_ucPort == COM2)
	{
		#if USART2_FIFO_EN == 1
			return &g_tUart2;
		#else
			return 0;
		#endif		
	}
	else if(_ucPort == COM3)
	{
		#if USART3_FIFO_EN == 1
			return &g_tUart3;
		#else
			return 0;
		#endif		
	}	
	else
	{
		return 0;
	}
}

/**
 * @brief		填写数据到Uart发送缓冲区，并使用中断方式发送
 * @param		_pUart: 串口指针
 * @param		ucBuf: 待发送的数据
 * @param		_usLen: 待发送的数据长度
 * @retval	None
 * @note		填写数据时使用锁机制,防止被打断
 */
static void UartSend(UART_T *_pUart, uint8_t *ucBuf, uint16_t _usLen)
{
	uint16_t i;

	for(i=0; i<_usLen; i++)
	{
		while(1)
		{
			// 定义为__IO型
			__IO uint16_t usCount;

			DISABLE_INT();
			usCount = _pUart->usTxCount;
			ENABLE_INT();

			if(usCount < _pUart->usTxBufSize)
			{
				break;
			}
		}
		// 将待发送的数据填入发送缓冲区
		_pUart->pTxBuf[_pUart->usTxWrite] = ucBuf[i];

		DISABLE_INT();
		// Write指针自加1
		if(++_pUart->usTxWrite >= _pUart->usTxBufSize)
		{
			_pUart->usTxWrite = 0;
		}
		// 等待发送的个数自加1
		_pUart->usTxCount++;
		ENABLE_INT();

		// 等待发送完成
		//while(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TC) == RESET);
		//while(huart1.gState != HAL_UART_STATE_READY);
		// 发送一个字节进一次发送中断	
		while(HAL_UART_Transmit_IT(_pUart->uartHandle, &ucBuf[i], 1) != HAL_OK);
	}

	//HAL_UART_Transmit_IT(_pUart->uartHandle, ucBuf, _usLen);
}

/**
 * @brief		串口发送函数
 * @param		_ucPort: COM口
 * @param		_ucBuf: 待发送的数据
 * @param		_usLen: 待发送的数据长度
 * @retval	None	
 * @note		调用UartSend()进行发送
 */
void comSendBuf(COM_PORT_E _ucPort, uint8_t *_ucBuf, uint16_t _usLen)
{
	UART_T *pUart;

	pUart = ComToUart(_ucPort);

	if(pUart == 0)
	{
		return ;
	}

	// 如果是485通信发送之前先切换到发送模式
	if(pUart->SendBefore != 0)
	{
		pUart->SendBefore(); 		
	}

	UartSend(pUart, _ucBuf, _usLen);
}

/**
 * @brief		发送一个字符数据
 * @param		_ucPort: COM口
 * @param		_ucByte: 待发送的字符数据
 * @retval	None	
 * @note		调用comSendBuf()函数
 */
void comSendChar(COM_PORT_E _ucPort, uint8_t _ucByte)
{
	comSendBuf(_ucPort, &_ucByte, 1);
}

/**
 * @brief		从COM口获取一个字节的数据
 * @param		_pUart: 串口指针
 * @param		_pByte: 存放获取数据的地址
 * @retval	0: 无数据 1: 有数据		
 * @note		
 */
static uint8_t UartGetChar(UART_T *_pUart, uint8_t *_pByte)
{
	uint16_t usCount;

	// 临界区保护
	DISABLE_INT();
	usCount = _pUart->usRxCount;		
	ENABLE_INT();

	// 无待接收的数据
	if(usCount == 0)
	{
		return 0;
	}
	else
	{
		// 从接收缓冲区中获取1个字符
		*_pByte = _pUart->pRxBuf[_pUart->usRxRead];

		DISABLE_INT();
		if(++_pUart->usRxRead >= _pUart->usRxBufSize)
		{
			_pUart->usRxRead = 0;
		}
		_pUart->usRxCount--;		// 该值在串口接收中断中累加
		ENABLE_INT();

		return 1;
	}
}

/**
 * @brief		从COM口获取一个字节的数据
 * @param		_ucPort: COM口
 * @param		_pByte: 存放获取数据的地址
 * @retval	0: 无数据 1: 有数据		
 * @note		调用UartGetChar()
 */
uint8_t comGetChar(COM_PORT_E _ucPort, uint8_t *_pByte)
{
	UART_T *pUart;

	pUart = ComToUart(_ucPort);

	if(pUart == 0)
	{
		return 0;
	}	

	return UartGetChar(pUart, _pByte);
}

/* 清除发送缓冲区 */
void comClearTxFifo(COM_PORT_E _ucPort)
{
	UART_T *pUart;

	pUart = ComToUart(_ucPort);
	
	if(pUart == 0)
	{
		return ;
	}		

	pUart->usTxWrite = 0;
	pUart->usTxRead = 0;
	pUart->usTxCount = 0;
}

/* 清除接收缓冲区 */
void comClearRxFifo(COM_PORT_E _ucPort)
{
	UART_T *pUart;

	pUart = ComToUart(_ucPort);
	
	if(pUart == 0)
	{
		return ;
	}		

	pUart->usRxWrite = 0;
	pUart->usRxRead = 0;
	pUart->usRxCount = 0;
}

/* 重够printf */
int fputc(int ch, FILE *f)
{
#if 1
	comSendChar(COM1, ch);

	return ch;
#else		// 采用阻塞方式发送数据,等待数据发送完毕
	HAL_USART_Transmit(&huart1, (uint8_t)&ch, 1, 0xFFFF);

	return ch;
#endif
}

/* 重定向scanf */
int fgetc(FILE *f)
{
#if 1
	uint8_t ucData;
	comGetChar(COM1, &ucData);
	
	return ucData;
#else
	uint8_t ch = 0;
	HAL_UART_Receive(&huart1, &ch, 1, 0xFFFF);

	return ch;
#endif
}


